package com.XSQS.storage.index;

import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.log4j.Logger;

import com.XSQS.storage.record.ARecord;
import com.XSQS.storage.record.ERecord;
import com.XSQS.storage.record.Record;
import com.XSQS.storage.record.TRecord;
import com.XSQS.storage.util.XMLSubTree;
import com.XSQS.util.Utility;

public class InvertedCreater {

	private Logger logger = Utility.getLogger(InvertedCreater.class);

	private static InvertedCreater instance;
	
	private InvertedWriter writer;

	private InvertedCreater() {
		writer=InvertedWriter.getInstance();
	}

	public synchronized static InvertedCreater getInstance() {
		if (instance == null) {
			instance = new InvertedCreater();
		}
		return instance;
	}

	public void createIndex(XMLSubTree xmlSubTree) {
		
		Map<String,List<IndexEntry>> itemMap=createItemMap(xmlSubTree);
		
		Set<String> keySet=itemMap.keySet();
		String[] keys = new String[keySet.size()];
		keys= keySet.toArray(keys);
		Arrays.sort(keys);
		
		writer.open();
		InvertedWordItem items[]=new InvertedWordItem[keys.length];
		int entryPos=1;
		for(int i=0;i<keys.length;++i){
			List<IndexEntry> entries=itemMap.get(keys[i]);
			items[i]=new InvertedWordItem();
			items[i].setKey(keys[i]);
			items[i].setFirstEntry(entryPos);
			items[i].setLastEntry(entryPos+entries.size()-1);
			for(int j=0;j<entries.size()-1;++j){
				entries.get(j).setNext(++entryPos);
			}
			entries.get(entries.size()-1).setNext(InvertedUtil.NULL);
			++entryPos;
			if(entryPos>=Integer.MAX_VALUE){
				logger.error("The size of IndexEntry is out of Integer.MAX_VALUE.");
			}
			writer.writeIndexEntries(entries);
		}
		writer.writeWordItems(items);
		writer.close();
	}
	
	protected Map<String,List<IndexEntry>> createItemMap(XMLSubTree xmlSubTree){
		Record[] records=xmlSubTree.getRecords();
		Map<String,List<IndexEntry>> itemMap=new HashMap<String,List<IndexEntry>>();
		for(Record record:records){
			if(record instanceof ARecord || record instanceof TRecord){
				String value=xmlSubTree.getValue(record);
				String[] words=sliptWords(value.trim());
				for(String word:words){
					IndexEntry entry=createIndexEntry(xmlSubTree, record);
					if(word==null||word.length()==0)continue;
					List<IndexEntry> indexEntries=itemMap.get(word);
					if(indexEntries==null){
						indexEntries=new LinkedList<IndexEntry>();
						itemMap.put(word, indexEntries);
					}
					indexEntries.add(entry);
				}
			}
		}
		return itemMap;
	}
	
	protected IndexEntry createIndexEntry(XMLSubTree xmlSubTree,Record record){
		IndexEntry entry=new IndexEntry();
		entry.setDocID(xmlSubTree.getDocID());
		entry.setSIndex(record.getSIndex());		
		entry.setType(record.getType());
		entry.setOffset(record.getOffset());
		entry.setLen(record.getLength());
		
		int parentID=record.getParent();
		ERecord parent=(ERecord) xmlSubTree.getRecords()[parentID-1];
		if(parent.getSIndex()!=parentID){
			logger.error("Error when getting the parent of the Record.  "+this.getClass().toString());
		}
		int nameID=parent.getNameID();
		entry.setParentID(parentID);
		entry.setNameID(nameID);
		entry.setNext(InvertedUtil.NULL);
		
		return entry;
	}
	
	private String[] sliptWords(String str){
		return str.split("\\W+");
	}
}
